/**
 * 
 */
package com.game.jumpdino.logic;

import java.util.List;
import java.util.Random;

import com.game.jumpdino.logic.visual.Blocker;
import com.game.jumpdino.logic.visual.HoldOnInformation;
import com.game.jumpdino.logic.visual.Holder;
import com.game.jumpdino.logic.visual.VisualBase;

/**
 * @author HugoGu
 *
 */
public class PlaygroundVisualFactory {	
	// Apply to all level.
	private static int minVerticalDistance; // device pixel.
	private static int maxVerticalDistance;
	private static Random mRand = new Random();
	
	@SuppressWarnings("static-access")
	public void fillInVisuals(Playground playground){
		updateLevelFromScore(playground.score);
		if (playground.getState() == GameState.Ready || playground.getState() == GameState.Started){
			fillInHolders(playground);
		   // fillInBlockers(playground); // dragon disable for now. 
		} else if (playground.getState() == GameState.Stoped){
			// Do nothing.
		}
	}
	
	private static void fillInBlockers(Playground playground){
		// You won't want to jump to a blocker. blocker is a dragon 
		if (playground.avatar != null && playground.avatar.getVelocity().y < -5){
			return;
		}
		
		if (playground.blockers.size() < VisualConfig.getBlockerConfig().maxShowUpCount){
			VisualBase visual = createVisualOn(0, selectVisualConfig(playground, VisualType.Blocker), playground);
			if (visual != null){ 
				playground.setMostTopBlocker((Blocker)visual);
			}
		}
	}
	
	@SuppressWarnings("static-access")
	private static void fillInHolders(Playground playground){
		float y = playground.getHeight();
		if (playground.getMostTopHolder() != null){
			y = playground.getMostTopHolder().getPosition().top;
		} else {
			// A new match. Generate a holder at middle.
		    Holder holder = (Holder)createVisualOn(Math.round(y), VisualConfig.getHolderConfig(), playground);
		    holder.move(-holder.getPosition().left, 0);
		    holder.move((playground.getWidth() - holder.getPosition().width()) / 2, 0);
		    y = holder.getPosition().top - minVerticalDistance;
		}
		
		VisualBase visual = null;
		float extraHeight = mRand.nextInt(maxVerticalDistance - minVerticalDistance) + minVerticalDistance;
		boolean breaked = false;
		while (y > extraHeight){
			y = y - extraHeight;
			VisualBase result = null ;
			if (breaked){
				result = createVisualOn(Math.round(y), selectVisualConfig(playground, VisualType.Holder, VisualType.KitHolder, VisualType.CloudHolder, VisualType.Umbrella), playground);
			} else {
				result = createVisualOn(Math.round(y), selectVisualConfig(playground, VisualType.Holder, VisualType.KitHolder, VisualType.CloudHolder, VisualType.BreakHolder, VisualType.Umbrella), playground);
			}
			if (result == null){
				continue;
			}
			visual = result;
			breaked = visual.getProperties().type == VisualType.BreakHolder;
			if (visual.getProperties().holdOn != null){
				HoldOnInformation hold = visual.getProperties().holdOn;
				Holder holder = (Holder)createVisualOn(hold.holderPosition.y + Math.round(y), hold.holder, playground);
				holder.move(hold.holderPosition.x - holder.getPosition().left + visual.getPosition().left, 0);
				holder.setVx(visual.getVelocity().x);
				holder.holdee = visual;
			}
			if (visual.getProperties().type == VisualType.BreakHolder){
				extraHeight = 0;
			} else {
				extraHeight = mRand.nextInt(maxVerticalDistance - minVerticalDistance) + minVerticalDistance;
			}
			y = visual.getPosition().top;
		}
		if (visual != null){
			playground.setMostTopHolder((Holder)visual);
		}
	}
	
	@SuppressWarnings("static-access")
	private static VisualConfig selectVisualConfig(Playground playground, VisualType... targetTypes){
		List<VisualConfig> configs = GameConfiguration.getConfigs(playground.score, targetTypes);
		float totalShowUpRate = 0.0f;
		VisualConfig select = null;
		if (configs.size() == 0){
			return null;
		}
		
		if (configs.size() > 1) {
			for (VisualConfig c : configs) {
				totalShowUpRate += c.showUpRate;
			}
			float target = mRand.nextFloat() * totalShowUpRate;

			totalShowUpRate = 0.0f;
			for (VisualConfig c : configs) {
				totalShowUpRate += c.showUpRate;
				if (totalShowUpRate >= target) {
					select = c;
					break;
				}
			}
		} else if (configs.size() == 1) {
			float target = mRand.nextFloat();
			select = configs.get(0);
			if (select.showUpRate < target){
				select = null;
			}
		}
		
		return select;
	}
	
	@SuppressWarnings("static-access")
	private static VisualBase createVisualOn(int y, VisualConfig config, Playground playground){
		if (config == null){
			return null;
		}
		
		if (playground.holders.size() > 0){
			// Don't generate the same type of holder continuous.
			if (config.type != VisualType.Holder && 
					config.type == playground.holders.get(playground.holders.size() - 1).getProperties().type){
				return null;
			}
		}
		
		if (mRand.nextInt(2) > 0){
			config.initialVelocity.x *= -1;
		}
		VisualBase visual = GameConfiguration.generateVisual(config, playground.getContext());
		visual.setPlayground(playground);
		int offsetX = playground.getWidth() - visual.getPosition().width();
		if (offsetX <= 0)
		{
			offsetX = 320 - visual.getPosition().width();
		}
		visual.move(mRand.nextInt(offsetX), y - visual.getPosition().height());
		if (visual instanceof Holder){
			playground.holders.add((Holder)visual);
		} else if (visual instanceof Blocker){
			playground.blockers.add((Blocker)visual);
		}
		config.showedCount++;
		return visual;
	}
	
	private static int updateLevelFromScore(int score){
		if (score < 2000){
			minVerticalDistance = 5;
			maxVerticalDistance = 10;
			return 1;
		} else if (score < 5000){
			minVerticalDistance = 10;
			maxVerticalDistance = 15;
			return 2;
		} else if (score < 10000){
			minVerticalDistance = 15;
			maxVerticalDistance = 20;
			return 3;
		} else if (score < 20000) {
			minVerticalDistance = 20;
			maxVerticalDistance = 25;
			return 4;
		} else {
			minVerticalDistance = 25;
			maxVerticalDistance = 30;
			return 5;
		}
	}
}
